package com.navinfo.platform.score;

import com.googlecode.aviator.Expression;
import com.googlecode.aviator.exception.CompileExpressionErrorException;
import com.navinfo.platform.score.enums.LoadStatusEnum;
import com.navinfo.platform.score.enums.ScoreLevelEnum;
import com.navinfo.platform.score.enums.SwitchStatus;
import com.navinfo.platform.score.pojo.ScoreEntry;
import com.navinfo.platform.score.pojo.ScoreWeight;
import com.navinfo.platform.score.pojo.ScoreWeightCompiled;
import com.navinfo.platform.score.service.ConfigLoadService;
import com.navinfo.platform.score.service.LoadScoreWeightService;
import com.navinfo.platform.score.service.impl.ConfigLoadServiceYaml;
import com.navinfo.platform.score.service.impl.LoadScoreWeightDBServiceImpl;
import com.navinfo.platform.score.service.impl.ScoreAlgorithmChainServiceImpl;
import com.navinfo.platform.score.utils.AviatorUtils;
import com.navinfo.tripanalysis.common.arithmetic.common.ExtraStatisticData;
import com.navinfo.tripanalysis.common.arithmetic.common.OuterStatisticData;
import org.apache.spark.broadcast.Broadcast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.util.*;

/**
 * 行程打分
 * @author CAI
 */
public class TripScoreService implements Serializable {

    static final Logger logger = LoggerFactory.getLogger(TripScoreService.class);
    /**
     * 载重场景开关
     */
    private static boolean loadSwitch;
    /**
     * 环境场景开关
     */
    private static boolean environmentSwitch;
    /**
     * 配置文件
     */
    private static Properties config;

    private static Map<String, ScoreWeightCompiled> scoreWeight = new HashMap<>();
    private static Map<String, ScoreWeight> scoreWeightMap = new HashMap<>();

    private static ScoreAlgorithmChainServiceImpl scoreService;
    private static boolean inited = false;
    /**
     * 初始化
     */
    public static void init(){
        File directory = null;
        try {
            directory = new File(TripScoreService.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
            InputStream in = new FileInputStream(directory.getAbsolutePath() + "/config_score.yaml");
            ConfigLoadService configService = new ConfigLoadServiceYaml().setInputStream(in);
            config = configService.load();
            // 载重场景开关
            loadSwitch = SwitchStatus.ON.toString().equalsIgnoreCase(config.getProperty("load.switch"));
            // 环境场景开关
            environmentSwitch = SwitchStatus.ON.toString().equalsIgnoreCase(config.getProperty("environment.switch"));
            logger.error("载重场景开关 : {}", loadSwitch);
            logger.error("环境场景开关 : {}", environmentSwitch);
            // 初期化 载重权重信息服务
            // 载重权重信息 服务
            LoadScoreWeightService loadWeightservice = createLoadScoreWeightService();

            Map<String, ScoreWeight> scoreWeightMap = loadWeightservice.getScoreWeight();
            scoreWeightMap.forEach((k,v)->{
                String key = k;
                ScoreWeight sw = v;
                // 过滤载重code 根据 载重场景开关
                if(loadSwitch){
                    // 载重开启时 过滤掉默认载重
                    // if(LoadStatusEnum.DEFAULT_LOAD.getType().equals(sw.getLoad_code())) return;
                } else {
                    // 载重未开启时 过滤掉默认载重以外的数据
                    if(!LoadStatusEnum.HALF_LOAD.getType().equals(sw.getLoad_code())) return;
                }
                // 环境场景开关
                if(!environmentSwitch){
                    if(ScoreLevelEnum.SAFE_LEVEL_ENV.getType().equals(sw.getLevel1())) return;
                }
                ScoreWeightCompiled swc = new ScoreWeightCompiled();
                Expression eps;
                try{
                    eps = AviatorUtils.compile(sw.getFormula());
                } catch (CompileExpressionErrorException e){
                    e.printStackTrace();
                    return;
                }
                swc.setScoreWeight(sw);
                swc.setExpression(eps);
                scoreWeight.put(key, swc);
            });
            // 初期化 分数计算服务
            createScoreService();
            inited = true;
        } catch (Exception e){
            inited = false;
            e.printStackTrace();
        }
    }

    private static LoadScoreWeightService createLoadScoreWeightService() {
        LoadScoreWeightDBServiceImpl loadWeightservice = new LoadScoreWeightDBServiceImpl();
        loadWeightservice.setDriver(config.getProperty("data.platform.jdbc.driver"));
        loadWeightservice.setUrl(config.getProperty("data.platform.jdbc.url"));
        loadWeightservice.setUser(config.getProperty("data.platform.jdbc.username"));
        loadWeightservice.setPass(config.getProperty("data.platform.jdbc.password"));
        loadWeightservice.setSql(config.getProperty("load.weights.sql"));
        logger.error("load.weights.sql {}",config.getProperty("load.weights.sql"));

        return loadWeightservice;
    }

    private static void createScoreService(){
        scoreService = new ScoreAlgorithmChainServiceImpl();
        scoreService.setBroadcastScoreWeight(scoreWeight);
        logger.error("权重信息 size  : {}, {}", scoreWeight.size(),scoreService.getBroadcastScoreWeight().size());
    }

    /**
     * 计算行程得分
     * @param tripData 行程数据
     * @param loadStatus 载重状态
     * @return 行程分数(1:综合得分, 2:安全得分, 3:经济得分)
     */
    public static List<ScoreEntry> calTripScore(OuterStatisticData tripData, String loadStatus, ExtraStatisticData extraData){
        if(!inited) return new ArrayList<>();
        LoadStatusEnum status = LoadStatusEnum.getEnum(loadStatus);
        if(status == null){
            status = LoadStatusEnum.HALF_LOAD;
            logger.error(" calTripScore loadStatus={}, wrong value。use default loadStatus value：{}", loadStatus, status.getType());
        }
        if(!loadSwitch){
            status = LoadStatusEnum.HALF_LOAD;
            logger.error(" calTripScore load is disable。use default load value：{}", loadStatus, status.getType());
        }
        List<ScoreEntry> scores = null;
        try{
            scores= scoreService.calTripScore(tripData, status, extraData);
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return scores;
    }
    /**
     * 计算行程得分
     * @param tripData 行程数据
     * @param loadStatus 载重状态
     * @return 行程分数(1:综合得分, 2:安全得分, 3:经济得分)
     */
    public static Map<String, Integer> calTripScoreMap(OuterStatisticData tripData, String loadStatus, ExtraStatisticData extraData){
        if(!inited) return null;
        LoadStatusEnum status = LoadStatusEnum.getEnum(loadStatus);
        if(status == null){
            status = LoadStatusEnum.HALF_LOAD;
            logger.error(" calTripScore loadStatus={}, wrong value。use default loadStatus value：{}", loadStatus, status.getType());
        }
        if(!loadSwitch){
            status = LoadStatusEnum.HALF_LOAD;
            logger.error(" calTripScore load is disable。use default load value：{}", loadStatus, status.getType());
        }
        Map<String, Integer> scores = null;
        try{
            List<ScoreEntry> scoreList = scoreService.calTripScore(tripData, status, extraData);
            scores = new HashMap<>();
            for(ScoreEntry entry : scoreList){
                if(ScoreLevelEnum.SCORE.getType().equals(entry.getType())){
                    scores.put("score", entry.getScore());
                }
                if(ScoreLevelEnum.SAFE_LEVEL.getType().equals(entry.getType())){
                    scores.put("safeScore", entry.getScore());
                }
                if(ScoreLevelEnum.ECO_LEVEL.getType().equals(entry.getType())){
                    scores.put("ecoScore", entry.getScore());
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return scores;
    }


    /**
     * 读取MYSQL中打分公式和打分权重
     * @return scoreWeight
     */

    public static Map<String, ScoreWeight> getLoadAndEnvironmentData() {
        File directory = null;
        try {
            directory = new File(TripScoreService.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
            InputStream in = new FileInputStream(directory.getAbsolutePath() + "/config_score.yaml");
            ConfigLoadService configService = new ConfigLoadServiceYaml().setInputStream(in);
            config = configService.load();
            // 载重场景开关
            loadSwitch = SwitchStatus.ON.toString().equalsIgnoreCase(config.getProperty("load.switch"));
            // 环境场景开关
            environmentSwitch = SwitchStatus.ON.toString().equalsIgnoreCase(config.getProperty("environment.switch"));
            logger.error("载重场景开关 : {}", loadSwitch);
            logger.error("环境场景开关 : {}", environmentSwitch);
            // 初期化 载重权重信息服务
            // 载重权重信息 服务
            LoadScoreWeightService loadWeightservice = createLoadScoreWeightService();


            scoreWeightMap = loadWeightservice.getScoreWeight();


        } catch (Exception e) {
            inited = false;
            e.printStackTrace();
        }
        return scoreWeightMap;
    }

    /**
     * 初始化打分服务
     * @param scoreMap
     */
    public static void init (Broadcast<Map<String, ScoreWeight>> scoreMap){

        scoreWeightMap = scoreMap.getValue();
        scoreWeightMap.forEach((k, v) -> {
            String key = k;
            ScoreWeight sw = v;
            // 过滤载重code 根据 载重场景开关
            if (loadSwitch) {
                // 载重开启时 过滤掉默认载重
                // if(LoadStatusEnum.DEFAULT_LOAD.getType().equals(sw.getLoad_code())) return;
            } else {
                // 载重未开启时 过滤掉默认载重以外的数据
                if (!LoadStatusEnum.HALF_LOAD.getType().equals(sw.getLoad_code())) return;
            }
            // 环境场景开关
            if (!environmentSwitch) {
                if (ScoreLevelEnum.SAFE_LEVEL_ENV.getType().equals(sw.getLevel1())) return;
            }
            ScoreWeightCompiled swc = new ScoreWeightCompiled();
            Expression eps;
            try {
                eps = AviatorUtils.compile(sw.getFormula());
                logger.info("----------eps----------:{}",eps);
            } catch (CompileExpressionErrorException e) {
                e.printStackTrace();
                return;
            }
            swc.setScoreWeight(sw);
            swc.setExpression(eps);
            scoreWeight.put(key, swc);
        });

        if(!scoreWeight.isEmpty()) {
            createScoreService();
            inited = true;
        }

    }

}


